package com.zlsy.secondtime.threadcoreknowledge.threadobjectclasscommonmethods;

import java.util.Date;
import java.util.LinkedList;


/**
 * @author zhouliang
 * @date 2020/5/17 14:54
 * 生产者消费者模式
 */
public class ProducterConsumerModel {

    public static void main(String[] args) {
        EventStoreage storeage = new EventStoreage();
        Producer producer = new Producer(storeage);
        Consumer consumer = new Consumer(storeage);

        Thread a = new Thread(producer);
        a.setName("生产者");
        Thread b = new Thread(consumer);
        b.setName("消费者");

        a.start();
        b.start();
    }
}

/**
 * 生产者
 */
class Producer implements Runnable {

    private EventStoreage storeage;

    public Producer(EventStoreage storeage) {
        this.storeage = storeage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storeage.put();
        }
    }
}

class EventStoreage {

    private int maxSize;
    private LinkedList<Date> storeage;

    public EventStoreage() {
        this.maxSize = 10;
        this.storeage = new LinkedList<>();
    }

    public synchronized void put() {
        //我们调用仓库的放入方法的时候，先判断当前仓库已经储存的数量，是否达到了最大的数量
        //如果达到了最大的数量，我们就释放当前锁对象 即：EventStoreage，
        // 然后进入blocked状态，等待消费者将生产唤醒。
        while (maxSize == storeage.size()) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果我们并没生产到仓库可以储存的最大数量，我们就往仓库里面放
        storeage.add(new Date());
        System.out.println("仓库里面已经有了" + storeage.size() + "个产品。");
        //放一次我们就唤醒当前在等待该对象monitor的线程
        this.notify();
    }

    public synchronized void take() {
        //我们消费的时候，先判断当前仓库是否已经被我们消费完了，
        // 如果消费完了，我们就放弃当前的对象锁，让生产者获取到锁，往仓库里放。
        while (storeage.size() == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("拿到了" + storeage.poll() + "，现在仓库还剩下：" + storeage.size());
        //只要消费一次我们就唤醒在等待该对象锁的线程
        //唤醒对象：可能是陷入阻塞的生产者，也可能是陷入阻塞的消费者
        this.notify();
    }
}

/**
 * 消费者
 */
class Consumer implements Runnable {

    private EventStoreage storeage;

    public Consumer(EventStoreage storeage) {
        this.storeage = storeage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storeage.take();
        }
    }
}